3 追蹤者

資料小工具

Yii 提供了一組可以用來顯示資料的 小工具。雖然 DetailView 小工具可以用來顯示單一記錄的資料,但 ListViewGridView 可以用來顯示資料記錄的列表或表格,並提供諸如分頁、排序和篩選等功能。

DetailView

DetailView 小工具顯示單一資料 模型 的詳細資訊。

它最適合用於以常規格式顯示模型(例如,每個模型屬性都顯示為表格中的一行)。模型可以是 yii\base\Model 的實例或子類別,例如 active record 或關聯陣列。

DetailView 使用 $attributes 屬性來決定應顯示哪些模型屬性以及應如何格式化它們。有關可用的格式化選項,請參閱格式化器章節

DetailView 的典型用法如下

echo DetailView::widget([
    'model' => $model,
    'attributes' => [
        'title',                                           // title attribute (in plain text)
        'description:html',                                // description attribute formatted as HTML
        [                                                  // the owner name of the model
            'label' => 'Owner',
            'value' => $model->owner->name,            
            'contentOptions' => ['class' => 'bg-red'],     // HTML attributes to customize value tag
            'captionOptions' => ['tooltip' => 'Tooltip'],  // HTML attributes to customize label tag
        ],
        'created_at:datetime',                             // creation date formatted as datetime
    ],
]);

請記住,與處理一組模型的 yii\widgets\GridView 不同,DetailView 僅處理一個模型。因此,大多數時候不需要使用閉包,因為 $model 是唯一要顯示的模型,並且在視圖中作為變數可用。

但是,在某些情況下,使用閉包可能很有用。例如,當指定 visible 並且您希望在評估為 false 時防止 value 計算時

echo DetailView::widget([
    'model' => $model,
    'attributes' => [
        [
            'attribute' => 'owner',
            'value' => function ($model) {
                return $model->owner->name;
            },
            'visible' => \Yii::$app->user->can('posts.owner.view'),
        ],
    ],
]);

ListView

ListView 小工具用於顯示來自 資料提供器 的資料。每個資料模型都使用指定的 視圖檔案 呈現。由於它提供了諸如分頁、排序和篩選等現成功能,因此它既可以用於向終端使用者顯示資訊,也可以用於建立資料管理 UI。

典型的用法如下

use yii\widgets\ListView;
use yii\data\ActiveDataProvider;

$dataProvider = new ActiveDataProvider([
    'query' => Post::find(),
    'pagination' => [
        'pageSize' => 20,
    ],
]);
echo ListView::widget([
    'dataProvider' => $dataProvider,
    'itemView' => '_post',
]);

_post 視圖檔案可能包含以下內容

<?php
use yii\helpers\Html;
use yii\helpers\HtmlPurifier;
?>
<div class="post">
    <h2><?= Html::encode($model->title) ?></h2>

    <?= HtmlPurifier::process($model->text) ?>    
</div>

在上面的視圖檔案中,目前的資料模型以 $model 的形式提供。此外,還提供以下變數

  • $key:混合型別,與資料項目關聯的鍵值。
  • $index:整數,資料項目在資料提供器傳回的項目陣列中的從零開始的索引。
  • $widget:ListView,此小工具實例。

如果您需要將其他資料傳遞到每個視圖,則可以使用 $viewParams 屬性來傳遞鍵值對,如下所示

echo ListView::widget([
    'dataProvider' => $dataProvider,
    'itemView' => '_post',
    'viewParams' => [
        'fullView' => true,
        'context' => 'main-page',
        // ...
    ],
]);

然後這些也將作為視圖中的變數提供。

GridView

資料網格或 GridView 是 Yii 最強大的小工具之一。如果您需要快速建構系統的管理部分,它非常有用。它從 資料提供器 取得資料,並使用一組 呈現每一行,以表格的形式呈現資料。

表格的每一行代表單一資料項目的資料,而欄通常代表項目的屬性(某些欄可能對應於屬性或靜態文字的複雜表示式)。

使用 GridView 所需的最少程式碼如下

use yii\grid\GridView;
use yii\data\ActiveDataProvider;

$dataProvider = new ActiveDataProvider([
    'query' => Post::find(),
    'pagination' => [
        'pageSize' => 20,
    ],
]);
echo GridView::widget([
    'dataProvider' => $dataProvider,
]);

上面的程式碼首先建立資料提供器,然後使用 GridView 顯示從資料提供器取得的每一行中的每個屬性。顯示的表格配備了現成的排序和分頁功能。

Grid 欄

網格表格的欄在 yii\grid\Column 類別方面進行組態,這些類別在 GridView 組態的 columns 屬性中組態。根據欄型別和設定,這些類別能夠以不同的方式呈現資料。預設類別是 yii\grid\DataColumn,它代表模型屬性,並且可以按其排序和篩選。

echo GridView::widget([
    'dataProvider' => $dataProvider,
    'columns' => [
        ['class' => 'yii\grid\SerialColumn'],
        // Simple columns defined by the data contained in $dataProvider.
        // Data from the model's column will be used.
        'id',
        'username',
        // More complex one.
        [
            'class' => 'yii\grid\DataColumn', // can be omitted, as it is the default
            'value' => function ($data) {
                return $data->name; // $data['name'] for array data, e.g. using SqlDataProvider.
            },
        ],
    ],
]);

請注意,如果未指定組態的 columns 部分,Yii 會嘗試顯示資料提供器模型的所有可能欄。

欄類別

網格欄可以使用不同的欄類別進行自訂

echo GridView::widget([
    'dataProvider' => $dataProvider,
    'columns' => [
        [
            'class' => 'yii\grid\SerialColumn', // <-- here
            // you may configure additional properties here
        ],

除了我們將在下面回顧的 Yii 提供的欄類別之外,您還可以建立自己的欄類別。

每個欄類別都從 yii\grid\Column 擴充,因此在組態網格欄時,您可以設定一些常用選項。

  • header 允許設定標題列的內容。
  • footer 允許設定頁腳列的內容。
  • visible 定義欄是否應可見。
  • content 允許您傳遞有效的 PHP 回呼,該回呼將傳回行的資料。格式如下

    function ($model, $key, $index, $column) {
        return 'a string';
    }
    

您可以通過將陣列傳遞到以下位置來指定各種容器 HTML 選項

資料欄

資料欄 用於顯示和排序資料。它是預設欄型別,因此在使用它時可以省略指定類別。

資料欄的主要設定是其 format 屬性。其值對應於 formatter 應用程式元件 中的方法,預設為 Formatter

echo GridView::widget([
    'columns' => [
        [
            'attribute' => 'name',
            'format' => 'text'
        ],
        [
            'attribute' => 'birthday',
            'format' => ['date', 'php:Y-m-d']
        ],
        'created_at:datetime', // shortcut format
        [
            'label' => 'Education',
            'attribute' => 'education',
            'filter' => ['0' => 'Elementary', '1' => 'Secondary', '2' => 'Higher'],
            'filterInputOptions' => ['prompt' => 'All educations', 'class' => 'form-control', 'id' => null]
        ],
    ],
]);

在上面,text 對應於 yii\i18n\Formatter::asText()。欄的值作為第一個引數傳遞。在第二個欄定義中,date 對應於 yii\i18n\Formatter::asDate()。欄的值再次作為第一個引數傳遞,而 'php:Y-m-d' 用作第二個引數值。

有關可用格式化器的列表,請參閱關於資料格式化的章節

對於組態資料欄,還有一個快捷方式格式,在 columns 的 API 文件中進行了描述。

使用 filterfilterInputOptions 來控制篩選器輸入的 HTML。

預設情況下,欄標題由 yii\data\Sort::link() 呈現。可以使用 yii\grid\Column::$header 進行調整。若要變更標題文字,您應該像上面的範例中一樣設定 yii\grid\DataColumn::$label。預設情況下,標籤將從資料模型中填入。有關更多詳細資訊,請參閱 yii\grid\DataColumn::getHeaderCellLabel()

操作欄

操作欄 顯示每一行的操作按鈕,例如更新或刪除。

echo GridView::widget([
    'dataProvider' => $dataProvider,
    'columns' => [
        [
            'class' => 'yii\grid\ActionColumn',
            // you may configure additional properties here
        ],

您可以組態的可用屬性為

  • controller 是應該處理操作的控制器的 ID。如果未設定,它將使用目前活動的控制器。
  • template 定義了用於組合操作欄中每個儲存格的範本。用大括號括起來的權杖被視為控制器操作 ID(在操作欄的上下文中也稱為按鈕名稱)。它們將被 buttons 中指定的相應按鈕呈現回呼取代。例如,權杖 {view} 將被回呼 buttons['view'] 的結果取代。如果找不到回呼,則權杖將被空字串取代。預設權杖為 {view} {update} {delete}
  • buttons 是一個按鈕呈現回呼陣列。陣列鍵是按鈕名稱(不帶大括號),值是相應的按鈕呈現回呼。回呼應使用以下簽名

    function ($url, $model, $key) {
        // return the button HTML code
    }
    

    在上面的程式碼中,$url 是欄為按鈕建立的 URL,$model 是為目前行呈現的模型物件,$key 是資料提供器陣列中模型的鍵。

  • urlCreator 是一個回呼,它使用指定的模型資訊建立按鈕 URL。回呼的簽名應與 yii\grid\ActionColumn::createUrl() 的簽名相同。如果未設定此屬性,則將使用 yii\grid\ActionColumn::createUrl() 建立按鈕 URL。
  • visibleButtons 是一個用於每個按鈕的可見性條件陣列。陣列鍵是按鈕名稱(不帶大括號),值是布林值 true/false 或匿名函式。當此陣列中未指定按鈕名稱時,預設情況下將顯示該按鈕。回呼必須使用以下簽名

    function ($model, $key, $index) {
        return $model->status === 'editable';
    }
    

    或者您可以傳遞布林值

    [
        'update' => \Yii::$app->user->can('update')
    ]
    

核取方塊欄

核取方塊欄 顯示核取方塊欄。

若要將 CheckboxColumn 新增至 GridView,請將其新增至 columns 組態,如下所示

echo GridView::widget([
    'id' => 'grid',
    'dataProvider' => $dataProvider,
    'columns' => [
        // ...
        [
            'class' => 'yii\grid\CheckboxColumn',
            // you may configure additional properties here
        ],
    ],

使用者可以按一下核取方塊以選取網格的行。可以透過呼叫以下 JavaScript 程式碼來取得選取的行

var keys = $('#grid').yiiGridView('getSelectedRows');
// keys is an array consisting of the keys associated with the selected rows

序號欄

序號欄 呈現從 1 開始並向前推進的行號。

用法與以下一樣簡單

echo GridView::widget([
    'dataProvider' => $dataProvider,
    'columns' => [
        ['class' => 'yii\grid\SerialColumn'], // <-- here
        // ...

排序資料

注意:本節正在開發中。

篩選資料

為了篩選資料,GridView 需要一個 模型,該模型代表搜尋條件,搜尋條件通常取自 GridView 表格中的篩選欄位。使用 active record 時的常見做法是建立一個搜尋模型類別,該類別提供所需的功能(可以由 Gii 為您產生)。此類別定義驗證規則,以在 GridView 表格上顯示篩選控制項,並提供一個 search() 方法,該方法將傳回資料提供器,其中包含已調整的查詢,用於處理搜尋條件。

若要為 Post 模型新增搜尋功能,我們可以建立一個 PostSearch 模型,如以下範例所示

<?php

namespace app\models;

use Yii;
use yii\base\Model;
use yii\data\ActiveDataProvider;

class PostSearch extends Post
{
    public function rules()
    { 
        // only fields in rules() are searchable
        return [
            [['id'], 'integer'],
            [['title', 'creation_date'], 'safe'],
        ];
    }

    public function scenarios()
    {
        // bypass scenarios() implementation in the parent class
        return Model::scenarios();
    }

    public function search($params)
    {
        $query = Post::find();

        $dataProvider = new ActiveDataProvider([
            'query' => $query,
        ]);

        // load the search form data and validate
        if (!($this->load($params) && $this->validate())) {
            return $dataProvider;
        }

        // adjust the query by adding the filters
        $query->andFilterWhere(['id' => $this->id]);
        $query->andFilterWhere(['like', 'title', $this->title])
              ->andFilterWhere(['like', 'creation_date', $this->creation_date]);

        return $dataProvider;
    }
}

提示:請參閱 查詢建構器,尤其是 篩選條件,以了解如何建構篩選查詢。

您可以在控制器中使用此函式來取得 GridView 的 dataProvider

$searchModel = new PostSearch();
$dataProvider = $searchModel->search(Yii::$app->request->get());

return $this->render('myview', [
    'dataProvider' => $dataProvider,
    'searchModel' => $searchModel,
]);

然後在視圖中,您可以將 $dataProvider$searchModel 指派給 GridView

echo GridView::widget([
    'dataProvider' => $dataProvider,
    'filterModel' => $searchModel,
    'columns' => [
        // ...
    ],
]);

獨立篩選表單

大多數時候,使用 GridView 標題篩選器就足夠了,但是如果您需要獨立的篩選表單,您也可以輕鬆地新增它。您可以建立具有以下內容的部分視圖 _search.php

<?php

use yii\helpers\Html;
use yii\widgets\ActiveForm;

/* @var $this yii\web\View */
/* @var $model app\models\PostSearch */
/* @var $form yii\widgets\ActiveForm */
?>

<div class="post-search">
    <?php $form = ActiveForm::begin([
        'action' => ['index'],
        'method' => 'get',
    ]); ?>

    <?= $form->field($model, 'title') ?>

    <?= $form->field($model, 'creation_date') ?>

    <div class="form-group">
        <?= Html::submitButton('Search', ['class' => 'btn btn-primary']) ?>
        <?= Html::submitButton('Reset', ['class' => 'btn btn-default']) ?>
    </div>

    <?php ActiveForm::end(); ?>
</div>

並將其包含在 index.php 視圖中,如下所示

<?= $this->render('_search', ['model' => $searchModel]) ?>

注意:如果您使用 Gii 產生 CRUD 程式碼,則預設情況下會產生獨立的篩選表單 (_search.php),但在 index.php 視圖中會註解掉。取消註解它,就可以使用了!

當您需要按不在 GridView 中顯示的欄位進行篩選或進行特殊篩選條件(例如日期範圍)時,獨立篩選表單非常有用。為了按日期範圍篩選,我們可以將非 DB 屬性 createdFromcreatedTo 新增到搜尋模型

class PostSearch extends Post
{
    /**
     * @var string
     */
    public $createdFrom;

    /**
     * @var string
     */
    public $createdTo;
}

search() 方法中擴充查詢條件,如下所示

$query->andFilterWhere(['>=', 'creation_date', $this->createdFrom])
      ->andFilterWhere(['<=', 'creation_date', $this->createdTo]);

並將代表性欄位新增到篩選表單

<?= $form->field($model, 'creationFrom') ?>

<?= $form->field($model, 'creationTo') ?>

使用模型關聯

在 GridView 中顯示 active record 時,您可能會遇到顯示相關欄的值的情況,例如文章作者的姓名,而不是僅顯示他的 id。您可以透過在 yii\grid\GridView::$columns 中將屬性名稱定義為 author.name 來執行此操作,當 Post 模型具有名為 author 的關聯,並且作者模型具有屬性 name 時。然後,GridView 將顯示作者的姓名,但預設情況下不啟用排序和篩選。您必須調整上一節中介紹的 PostSearch 模型,以新增此功能。

若要在相關欄上啟用排序,您必須聯結相關表格,並將排序規則新增至資料提供器的排序元件

$query = Post::find();
$dataProvider = new ActiveDataProvider([
    'query' => $query,
]);

// join with relation `author` that is a relation to the table `users`
// and set the table alias to be `author`
$query->joinWith(['author' => function($query) { $query->from(['author' => 'users']); }]);
// since version 2.0.7, the above line can be simplified to $query->joinWith('author AS author');
// enable sorting for the related column
$dataProvider->sort->attributes['author.name'] = [
    'asc' => ['author.name' => SORT_ASC],
    'desc' => ['author.name' => SORT_DESC],
];

// ...

篩選也需要如上所述的 joinWith 呼叫。您還需要在屬性和規則中定義可搜尋的欄,如下所示

public function attributes()
{
    // add related fields to searchable attributes
    return array_merge(parent::attributes(), ['author.name']);
}

public function rules()
{
    return [
        [['id'], 'integer'],
        [['title', 'creation_date', 'author.name'], 'safe'],
    ];
}

然後在 search() 中,您只需新增另一個篩選條件

$query->andFilterWhere(['LIKE', 'author.name', $this->getAttribute('author.name')]);

資訊:在上面,我們對關聯名稱和表格別名使用相同的字串;但是,當您的別名和關聯名稱不同時,您必須注意在何處使用別名,以及在何處使用關聯名稱。一個簡單的規則是在每個用於建構資料庫查詢的地方使用別名,在所有其他定義(例如 attributes()rules() 等)中使用關聯名稱。

例如,如果您對作者關聯表格使用別名 au,則 joinWith 陳述式如下所示

$query->joinWith(['author au']);

當在關聯定義中定義了別名時,也可以只呼叫 $query->joinWith(['author']);

別名必須在篩選條件中使用,但屬性名稱保持不變

$query->andFilterWhere(['LIKE', 'au.name', $this->getAttribute('author.name')]);

排序定義也是如此

$dataProvider->sort->attributes['author.name'] = [
     'asc' => ['au.name' => SORT_ASC],
     'desc' => ['au.name' => SORT_DESC],
];

此外,當指定排序的 defaultOrder 時,您需要使用關聯名稱而不是別名

$dataProvider->sort->defaultOrder = ['author.name' => SORT_ASC];

資訊:有關 joinWith 和背景中執行的查詢的更多資訊,請查看關於與關聯聯結的 active record 文件

使用 SQL 視圖進行篩選、排序和顯示資料

還有另一種方法可以更快且更有用 - SQL 視圖。例如,如果我們需要顯示包含使用者及其設定檔的 gridview,我們可以透過這種方式執行

CREATE OR REPLACE VIEW vw_user_info AS
    SELECT user.*, user_profile.lastname, user_profile.firstname
    FROM user, user_profile
    WHERE user.id = user_profile.user_id

然後,您需要建立將代表此視圖的 ActiveRecord


namespace app\models\views\grid;

use yii\db\ActiveRecord;

class UserView extends ActiveRecord
{

    /**
     * {@inheritdoc}
     */
    public static function tableName()
    {
        return 'vw_user_info';
    }

    public static function primaryKey()
    {
        return ['id'];
    }

    /**
     * {@inheritdoc}
     */
    public function rules()
    {
        return [
            // define here your rules
        ];
    }

    /**
     * {@inheritdoc}
     */
    public function attributeLabels()
    {
        return [
            // define here your attribute labels
        ];
    }


}

之後,您可以使用此 UserView active record 和搜尋模型,而無需額外指定排序和篩選屬性。所有屬性都將開箱即用。請注意,此方法有一些優缺點

  • 您無需指定不同的排序和篩選條件。一切都開箱即用;
  • 由於資料大小、執行的 sql 查詢計數(對於每個關聯,您都不需要任何額外查詢),因此它可能會快得多;
  • 由於這只是 sql 視圖上的簡單對應 UI,因此它缺少實體中的一些網域邏輯,因此如果您有一些方法(例如 isActiveisDeleted 或其他會影響 UI 的方法),您也需要在這個類別中複製它們。

單一頁面上的多個 GridView

您可以在單一頁面上使用多個 GridView,但需要進行一些額外組態,以使其彼此不干擾。當使用 GridView 的多個實例時,您必須為產生的排序和分頁連結組態不同的參數名稱,以便每個 GridView 都有自己的個別排序和分頁。您可以透過設定 dataProvider 的 sortpagination 實例的 sortParampageParam 來執行此操作。

假設我們要列出 PostUser 模型,我們已經在 $userProvider$postProvider 中準備了兩個資料提供器

use yii\grid\GridView;

$userProvider->pagination->pageParam = 'user-page';
$userProvider->sort->sortParam = 'user-sort';

$postProvider->pagination->pageParam = 'post-page';
$postProvider->sort->sortParam = 'post-sort';

echo '<h1>Users</h1>';
echo GridView::widget([
    'dataProvider' => $userProvider,
]);

echo '<h1>Posts</h1>';
echo GridView::widget([
    'dataProvider' => $postProvider,
]);

將 GridView 與 Pjax 結合使用

Pjax 小工具允許您更新頁面的特定區段,而不是重新載入整個頁面。當使用篩選器時,您可以使用它僅更新 GridView 內容。

use yii\widgets\Pjax;
use yii\grid\GridView;

Pjax::begin([
    // PJax options
]);
    Gridview::widget([
        // GridView options
    ]);
Pjax::end();

Pjax 也適用於 Pjax 小工具內的連結和 Pjax::$linkSelector 指定的連結。但這對於 ActionColumn 的連結來說可能是一個問題。若要防止這種情況,請在編輯 ActionColumn::$buttons 屬性時,將 HTML 屬性 data-pjax="0" 新增至連結。

Gii 中搭配 Pjax 的 GridView/ListView

自 2.0.5 起,Gii 的 CRUD 產生器有一個名為 $enablePjax 的選項,可以透過 Web 介面或命令列使用。

yii gii/crud --controllerClass="backend\\controllers\PostController" \
  --modelClass="common\\models\\Post" \
  --enablePjax=1

它會產生一個 Pjax 小工具,用於包裝 GridViewListView 小工具。

延伸閱讀

發現錯字或您認為此頁面需要改進?
在 github 上編輯 !